import { EventEmitter } from '../../helpers/EventEmitter'
import { RedisClientEnhance } from './RedisClientEnhance'

export class RedisPool extends EventEmitter {

  private validConnections: RedisClientEnhance[] = []
  private totalCount: number = 0
  private inUseConnections: RedisClientEnhance[] = []
  private count!: number
  // NOTE: 超时时间为 10s
  private timeout: number = 10 * 1000
  constructor(count: number = 3) {
    super()
    this.count = count
  }

  public async hget(key: string, field: string) {
    const client = await this.getAvailableClient()
    const result = client.do().hget(key, field)
    this.release(client)
    return result
  }

  public async hmget(key: string, fields: string[]) {
    const client = await this.getAvailableClient()
    const res = client.do().hmget(key, ...fields)
    this.release(client)
    return res
  }

  public async get(key: string) {
    const client = await this.getAvailableClient()
    const res = client.do().get(key)
    this.release(client)
    return res
  }

  public async set(key: string, value: string) {
    const client = await this.getAvailableClient()
    const result = client.do().set(key, value)
    this.release(client)
    return result
  }

  public async hset() {

  }

  public async hdel() {

  }

  private release(c: RedisClientEnhance) {
    c.release()
    this.validConnections.push(c)
  }

  private async getAvailableClient(): Promise<RedisClientEnhance> {
    const now = Date.now()
    if (this.validConnections.length === 0) {
      if (this.totalCount <= this.count) {
        const client = new RedisClientEnhance()
        this.inUseConnections.push(client)
        this.totalCount++
        return client
      } else {
        return this.waitGetClient(now)
      }
    }
    return this.validConnections.pop()!
  }

  private async waitGetClient(now: number): Promise<RedisClientEnhance> {
    return new Promise((ok, fail) => {
      let count = 1
      let client = this.validConnections.pop()
      if (client) {
        return ok(client)
      }
      const iter = () => {
        const d = Date.now()
        const delay = Math.floor((d - now) / count * 5)
        if (d - now > this.timeout || d + delay - now > Math.ceil(this.timeout / count)) {
          return fail('connect timeout')
        }
        setTimeout(() => {
          client = this.validConnections.pop()
          if (!client) {
            count++
            return iter()
          }
          ok(client)
        }, delay)
      }
      iter()
    })
  }

}
